/**
 *  Copyright 2006-2007 EmbedChat
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.embedchat.desktop.client;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoHandler;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.embedchat.desktop.Observer;
import org.embedchat.desktop.StateModel;
import org.embedchat.protocol.codec.ChatProtocolCodecFactory;
import org.embedchat.protocol.message.client.LoginMessage;
import org.embedchat.protocol.message.client.RoomsListRequestMessage;
import org.embedchat.protocol.message.common.TextMessage;

public class Client extends Observer {
	
	private final Logger log = LoggerFactory.getLogger(Client.class);
	
	private StateModel model;
	private static final int CONNECT_TIMEOUT = 30;
	private IoHandler handler;
	private IoSession session = null;

	public Client(StateModel model) {
		this.model = model;
	}
	
	public void connect() {
		try {
	        SocketConnector connector = new SocketConnector();
	        // Change the worker timeout to 1 second to make the I/O thread quit soon
	        // when there's no connection to manage.
	        connector.setWorkerTimeout( 1 );
	        SocketConnectorConfig cfg = new SocketConnectorConfig();
	        cfg.setConnectTimeout(CONNECT_TIMEOUT);
            cfg.getFilterChain().addLast(
                    "codec",
                    new ProtocolCodecFilter( new ChatProtocolCodecFactory(false) )
            );
	        cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
	        
			handler = new ClientSessionHandler(model);
			SocketAddress address = new InetSocketAddress(
					model.getConnectionHost(),
					model.getConnectionPort()
			);
			ConnectFuture future = connector.connect(
					address,
					handler,
					cfg
			);
			future.join();
			session = future.getSession();
			sendRoomListRequestMessage();
			model.changeState(StateModel.CONNECTION_ESTABLISHED_STATE);
		} catch (Exception e) {
			log.error("Fail to connect");
			model.setErrorMessage("Fail to connect");
			model.setRequestForError(true);
			model.update();			
		}
	}
	
	public void login() {
		LoginMessage message = new LoginMessage();
		message.setLogin(model.getLoginName());
		message.setPassword(model.getPassword());
		message.setRoomId(model.getSelectedRoomId());
		session.write(message);
	}
	
	public void disconnect() {
		if (session != null) {
			session.close();
			session = null;
		}
	}
	
	public void reconnect() {
		disconnect();
		connect();
	}
	
	public void sendTextMessage(String messageStr) {
		TextMessage message = new TextMessage();
		message.setMessageString(messageStr);
		session.write(message);
	}
	
	public void sendRoomListRequestMessage() {
		RoomsListRequestMessage message = new RoomsListRequestMessage();
		message.setRequestString("test");
		session.write(message);
	}

	@Override
	public void update() {
		if(model.isRequestForConnect()) {
			model.setRequestForConnect(false);			
			try {
				connect();				
			} catch (Throwable e) {
				log.error(e.getMessage());
			}
		}
		if(model.isRequestForDisconnect()) {
			model.setRequestForDisconnect(false);			
			if (session != null) {
				session.close();
				session = null;
			}
		}
		if(model.isRequestForLogin()) {
			model.setRequestForLogin(false);			
			try {
				if(StateModel.CONNECTION_ESTABLISHED_STATE == model.getModelState()) {
					login();					
				} else {
					connect();
					login();					
				}
			} catch (Throwable e) {
				log.error(e.getMessage());
			}
		}
		if(model.isRequestForSendMessage()) {
			model.setRequestForSendMessage(false);			
			if (session != null) {
				sendTextMessage(model.getSendMessage());				
			}
		}
		if(model.isRequestForReconnect()) {
			model.setRequestForReconnect(false);			
			try {
				log.debug("reconnecting");
				reconnect();				
			} catch (Throwable e) {
				log.error(e.getMessage());
			}
		}
	}
	
}
